local mysql = require "libs/lua-resty-mysql/lib/resty/mysql"



local mysql_util = {}

local tacer_db_util = require("skywalking.plugins.db_util")


mysql_util.exec = function(server_name, sql)
    if not (server_name and sql) then
        nlog.error("mysql exec error, server_name=" .. tostring(server_name) .. ", sql=" .. tostring(sql))
        return nil, "not (server_name and sql)"
    end
    if not mysql_conf[server_name] then
        nlog.error("server_name undefined, server_name = " .. tostring(server_name))
        return nil, "server_name undefined"
    end
    local mc = mysql_conf[server_name]
    

    local db, new_err = mysql:new()
    if PRODUCT_ENV ~= "develop" then
        tacer_db_util.MysqlBefore(db, mc)
    end
    
    if not db then
        tacer_db_util.MysqlAfter(db, "", new_err)
        nlog.error("failed to instantiate mysql: ", new_err)
        return nil, new_err
    end

    db:set_timeout(mc.connect_timeout)
    local connect_ok, connect_err, connect_errcode, connect_sqlstate = db:connect{
        host = mc.host,
        port = mc.port,
        database = mc.database,
        user = mc.user,
        password = mc.password,
        charset = mc.charset,
        max_packet_size = mc.max_packet_size
    }

    if not connect_ok then
        tacer_db_util.MysqlAfter(db, sql, connect_err)
        nlog.error("connect failed: " .. cjson.encode(connect_err) .. ";"
                   .. cjson.encode(connect_errcode) .. ";" .. cjson.encode(connect_sqlstate)
                   .. ";" .. cjson.encode(mc))
        return nil, connect_err
    end

    local res, query_err, query_errcode, query_sqlstate = db:query(sql)
    if not res then
        tacer_db_util.MysqlAfter(db, sql, query_err)
        nlog.error("query failed: " .. cjson.encode(query_err) .. ";"
                   .. cjson.encode(query_errcode) .. ";" .. cjson.encode(query_sqlstate)
                   .. ";" .. cjson.encode(sql))
        return nil, query_err
    end

    local set_keepalive_ok, set_keepalive_err = db:set_keepalive(10000, 100)
    if not set_keepalive_ok then
        tacer_db_util.MysqlAfter(db, sql, set_keepalive_err)
        nlog.error("failed to set keepalive: ", set_keepalive_err)
        return nil, set_keepalive_err
    end

    tacer_db_util.MysqlAfter(db, sql)
    return res, nil
end


mysql_util.exec_transaction = function(server_name, sql_list)
    local nlog = nlog
    local cjson = cjson
    local mysql_conf = mysql_conf
    if not (server_name and sql_list) then
        nlog.error("mysql exec_transaction error, server_name=" .. tostring(server_name)
                   .. ", sql_list=" .. tostring(sql_list))
        return false, "not (server_name and sql_list)"
    end
    if not mysql_conf[server_name] then
        nlog.error("server_name undefined, server_name = " .. tostring(server_name))
        return false, "server_name undefined"
    end
    local mc = mysql_conf[server_name]

    local db, new_err = mysql:new()
    if not db then
        nlog.error("failed to instantiate mysql: ", new_err)
        return false, new_err
    end

    db:set_timeout(mc.connect_timeout)
    local connect_ok, connect_err, connect_errcode, connect_sqlstate = db:connect{
        host = mc.host,
        port = mc.port,
        database = mc.database,
        user = mc.user,
        password = mc.password,
        charset = mc.charset,
        max_packet_size = mc.max_packet_size,
        pool = mc.pool
    }

    if not connect_ok then
        nlog.error("connect failed: " .. cjson.encode(connect_err) .. ";"
                   .. cjson.encode(connect_errcode) .. ";" .. cjson.encode(connect_sqlstate)
                   .. ";" .. cjson.encode(mc))
        return false, connect_err
    end

    local begin_res, begin_query_err, begin_query_errcode, begin_query_sqlstate = db:query("BEGIN;")
    if begin_query_err then
        nlog.error("query failed begin_res: " .. cjson.encode(begin_res) .. ";"
                   .. cjson.encode(begin_query_err) .. ";" .. cjson.encode(begin_query_errcode)
                   .. ";" .. cjson.encode(begin_query_sqlstate) .. ";" .. cjson.encode(sql_list))
        return false, begin_query_err
    end

    for _, one_sql in ipairs(sql_list) do
        local res, query_err, query_errcode, query_sqlstate = db:query(one_sql)
        nlog.info("query res: " .. cjson.encode(res) .. ";"
                  .. cjson.encode(query_err) .. ";" .. cjson.encode(query_errcode)
                  .. ";" .. cjson.encode(query_sqlstate).. ";" .. cjson.encode(one_sql))
        if query_err then
            nlog.error("query failed res: " .. cjson.encode(res) .. ";"
                       .. cjson.encode(query_err) .. ";" .. cjson.encode(query_errcode)
                       .. ";" .. cjson.encode(query_sqlstate) .. ";" .. cjson.encode(one_sql))
            local rb_res, rb_query_err, rb_query_errcode, rb_query_sqlstate = db:query("ROLLBACK;")
            if rb_query_err then
                nlog.error("query failed rollback_res: " .. cjson.encode(rb_res) .. ";"
                           .. cjson.encode(rb_query_err) .. ";" .. cjson.encode(rb_query_errcode)
                           .. ";" .. cjson.encode(rb_query_sqlstate) .. ";" .. cjson.encode(one_sql))
            end
            return false, begin_query_err
        end
    end

    local commit_res, commit_query_err, commit_query_errcode, commit_query_sqlstate = db:query("COMMIT;")
    if commit_query_err then
        nlog.error("query failed commit_res: " .. cjson.encode(commit_res) .. ";"
                   .. cjson.encode(commit_query_err) .. ";" .. cjson.encode(commit_query_errcode)
                   .. ";" .. cjson.encode(commit_query_sqlstate) .. ";" .. cjson.encode(sql_list))
        return false, commit_query_err
    end

    local set_keepalive_ok, set_keepalive_err = db:set_keepalive(10000, 100)
    nlog.info("set_keepalive set_keepalive_ok: " .. cjson.encode(set_keepalive_ok) .. ";"
              .. cjson.encode(set_keepalive_err) .. ";" .. cjson.encode(set_keepalive_err))
    if not set_keepalive_ok then
        nlog.error("failed to set keepalive: ", set_keepalive_err)
        return false, set_keepalive_err
    end
    return true
end


return mysql_util
